#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <string>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include "log.hpp"

using std::cout;
using std::endl;

log lg;

enum
{
    SOCK_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    FORK_ERR
};

class tcpserver
{
public:
    tcpserver(const uint16_t &port, const std::string &ip = "0.0.0.0")
        : _socketfd(-1), _port(port), _ip(ip)
    {
    }

    ~tcpserver()
    {
        if (_socketfd >= 0)
        {
            close(_socketfd);
        }
    }

    void Init()
    {
        // 创建套接字
        _socketfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_socketfd == -1) // 套接字创建失败
        {
            lg.logmessage(Fatal, "create socket. errno: %d, describe: %s", errno, strerror(errno));
            exit(SOCK_ERR);
        }
        lg.logmessage(Info, "create socket. errno: %d, describe: %s", errno, strerror(errno));

        // 绑定套接字
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);             // 注意：要转化为网络字节序
        inet_aton(_ip.c_str(), &(local.sin_addr)); // 除了inet_addr函数可以将字符串转化为uint32，也可以还用inet_aton
        int n = bind(_socketfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0) // 绑定失败
        {
            lg.logmessage(Fatal, "bind socket. errno: %d, describe: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg.logmessage(Info, "bind socket. errno: %d, describe: %s", errno, strerror(errno));

        // 监听（一般配合socket和bind函数使用）
        int l = listen(_socketfd, 5);
        if (l == -1)
        {
            lg.logmessage(Fatal, "listen socket. errno: %d, describe: %s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        lg.logmessage(Info, "listen socket. errno: %d, describe: %s", errno, strerror(errno));
    }

    // 回显
    void v1(int acfd, const std::string &ipbuffer, const uint16_t &client_port)
    {
        char buffer[4096];
        while (true)
        {
            ssize_t n = read(acfd, buffer, sizeof(buffer));
            if (n > 0) // 读取成功
            {
                buffer[n] = 0; // 当做字符串
                // 客户端接收打印
                cout << "client say# " << buffer << endl;
                // 回显给客户端
                std::string echo_string = "tcp-server echo# ";
                echo_string += buffer;
                write(acfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                // 当客户端退出，意味着服务器没有任何数据可读，那么我们这里规定服务器也退出
                lg.logmessage(Info, "%s:%d quit, server close...", ipbuffer.c_str(), client_port);
                break;
            }
            else // n < 0
            {
                // 读取错误
                lg.logmessage(Warning, "server read error...");
                break;
            }
        }
    }

    void Run()
    {
        // 方法二：忽略SIGCHLD信号
        signal(SIGCHLD, SIG_IGN);

        // 服务器启动后是周而复始的在工作的，因此服务器本质就是死循环
        while (true)
        {
            // 获取连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            memset(&client, 0, sizeof(client));
            int acfd = accept(_socketfd, (struct sockaddr *)&client, &len);
            if (acfd < 0)
            {
                lg.logmessage(Warning, "accept socket. errno: %d, describe: %s", errno, strerror(errno));
                continue;
            }
            lg.logmessage(Info, "accept socket. errno: %d, describe: %s", errno, strerror(errno));

            char ipbuffer[32];
            uint16_t client_port = ntohs(client.sin_port);
            inet_ntop(AF_INET, &(client.sin_addr), ipbuffer, sizeof(ipbuffer));

            // v1 - 单进程版
            // v1(acfd, ipbuffer, client_port);
            // close(acfd); // 关闭

            // v2 - 多进程版
            pid_t pid = fork();
            if (pid == 0) // child
            {
                // 子进程提供服务
                close(_socketfd);
                // 方法一: 子进程再创建孙子进程
                // if (fork() > 0)
                // {
                //     exit(0);
                // }
                v1(acfd, ipbuffer, client_port);
                close(acfd); // 关闭
                exit(0);
            }
            else if (pid > 0) // parent
            {
                // 父进程继续获取新连接
                close(acfd);

                pid_t rid = waitpid(pid, nullptr, 0); // 父进程回收子进程
                (void)rid;
            }
            else // 子进程创建失败
            {
                lg.logmessage(Fatal, "fork child. errno: %d, describe: %s", errno, strerror(errno));
                exit(FORK_ERR);
            }
        }
    }

private:
    int _socketfd; // 套接字文件描述符
    uint16_t _port;
    std::string _ip;
};
